<template>
  <q-field v-model="values" :hint="hintTips" :label="undefined" :loading='loading' :readonly="readonly" borderless
           class="q-picture" lazy-rules="ondemand" no-error-icon v-bind="$attrs">
    <template v-slot:control>
      <q-uploader :factory="factoryFn" :filter="filterFile" v-bind="uploadBind" @added="added" @failed="failed"
                  @finish="finishUpload" @start="startUpload" @uploaded="uploaded">
        <template v-slot:header="scope">
          {{ getFiles(scope) }}
          <div class="row items-start q-gutter-sm">
            <template v-for="(file, i) in scope.files" :key="file.__key">
              <div class="q-pictrue-card no-border rounded-borders shadow-1 overflow-hidden flex-center cursor-pointer"
                   @click.stop="viewPicture(file, i)">
                <q-img v-if="file.__img" :src="file.__img.src" fit="fill" loading="lazy" spinner-size="2rem">
                  <div v-if="file.__status === 'failed'" class="uploader-error rounded-borders absolute-top-right"
                       @click.stop="viewPicture">
                    <q-icon class="cursor-pointer" color="negative" name="cloud_upload" @click="scope.upload">
                      <q-tooltip v-bind="$tooltipAttr">{{ `${$t('UploadFailed')}` }}</q-tooltip>
                    </q-icon>
                  </div>
                  <div v-if="file.__status === 'uploading'" class="absolute-full">
                    <q-inner-loading :showing="true">
                      <q-circular-progress :value="file.__progress * 100" color="primary" size="3.5rem" />
                    </q-inner-loading>
                  </div>
                  <div v-else class="q-pictrue-tip absolute-bottom flex justify-around items-center"
                       @click.stop="viewPicture">
                    <q-icon class="cursor-pointer" name="cloud_download" @click="downLoadFile(file)"></q-icon>
                    <q-icon class="cursor-pointer" name="delete" @click="deleteFile(file, i)"></q-icon>
                  </div>
                  <template v-slot:error>
                    <div class="q-pictrue-tip absolute-bottom flex justify-around items-center"
                         @click.stop="viewPicture">
                      <q-icon class="cursor-pointer" name="cloud_download" @click="downLoadFile(file)"></q-icon>
                      <q-icon class="cursor-pointer" name="delete" @click="deleteFile(file, i)"></q-icon>
                    </div>
                  </template>
                </q-img>
              </div>
            </template>
            <template v-if="scope.canAddFiles && !readonly">
              <template v-if="!btnSlot">
                <q-btn class="q-pictrue-add-btn" icon="add" padding="2.75rem" text-color="primary"
                       @click="scope.pickFiles">
                  <q-uploader-add-trigger />
                  <q-tooltip v-bind="$tooltipAttr">{{ `${$t('PleaseSelect')}${$t('Picture')}` }}</q-tooltip>
                </q-btn>
              </template>
              <div v-else @click="scope.pickFiles">
                <q-uploader-add-trigger />
                <slot name="btnSlot"></slot>
                <q-tooltip v-bind="$tooltipAttr">{{ `${$t('PleaseSelect')}${$t('Picture')}` }}</q-tooltip>
              </div>
            </template>
          </div>
        </template>
        <template v-slot:list></template>
      </q-uploader>
    </template>
  </q-field>
  <q-inner-loading :showing="innerLoading">
    <q-spinner-ios color="primary" size="30px" />
  </q-inner-loading>
  <q-preview v-model="dialogVisible" :imgList="(uploadScope || {}).files" :index="pictureIndex"
             keyWords="__img.src"></q-preview>
</template>

<script setup>
import { baseURL } from 'boot/env'
import { i18n } from 'boot/i18n'
import QPreview from 'components/Global/Tools/QPreview.vue'
import { format } from 'quasar'
import { postAPI } from 'src/api'
import { responseHandle } from 'src/axios/utils'
import { createDialog, exportedStreamFile } from 'src/utils/utils'
import { userStores } from 'stores/user-stores'
import { computed, ref, watch } from 'vue'

const { humanStorageSize } = format

const props = defineProps({
  autoUpload: { type: Boolean, default: true }, lazyRules: [Boolean, String], formatAdded: Function,
  publicRead: Boolean, modelValue: String, clearable: Boolean, required: Boolean, hideHint: Boolean,
  outlined: Boolean, readonly: Boolean, loading: Boolean, btnSlot: Boolean, change: Function, accept: String,
  sName: String, bName: String,
  // 最大允许上传个数
  limitNum: { type: Number, default: 5 },
  // 单文件最大限制
  isLtXM: { type: Number, default: 50 }
})
const emits = defineEmits(['update:modelValue', 'change'])

const values = ref('')
const pictureIndex = ref(0)
const uploadScope = ref(null)
const innerLoading = ref(false)
const dialogVisible = ref(false)

const maxLtXM = computed(() => props.isLtXM * 1024 * 1024)
const url = computed(() => `${baseURL}oss/api/ossUploadRecord/batchUpload`)
const uploadHeader = computed(() => {
  return [{ name: 'Authorization', value: userStores().token }, { name: 'lang', value: i18n.locale }]
})
const hintTips = computed(() => {
  const { hideHint, limitNum, isLtXM, readonly, accept = '' } = props
  if (readonly || hideHint) return undefined
  if (limitNum && limitNum !== Infinity) return `${i18n.t('PictureTip1', [limitNum, isLtXM, accept])}`
  return `${i18n.t('PictureTip2', [isLtXM])}`
})
const uploadBind = computed(() => {
  const { accept = 'image/*', autoUpload = true } = props
  return {
    flat: true, batch: true, multiple: true, label: undefined, accept, color: 'transparent', readonly: props.readonly,
    'auto-upload': autoUpload, 'max-files': props.limitNum, headers: uploadHeader.value, 'max-file-size': maxLtXM.value
  }
})

const filterFile = files => {
  const list = uploadScope.value.files.map(el => el.__key)
  return files.filter(el => !list.includes(`${el.name}${el.size}`))
}
const formatFile = file => {
  const tempFile = {
    id: file.id, lastModified: +file.endTime, name: file.originalFileName, size: file.sizeBytes,
    __progressLabel: '100.00%', __status: 'uploaded', resultPath: file.resultPath,
    __sizeLabel: file.sizeBytes < 1024 ? `${file.sizeBytes}.0B` : humanStorageSize(file.sizeBytes)
  }
  tempFile.__key = `${file.originalFileName}${file.sizeBytes}`
  if (file.imageFlag === '1') tempFile.__img = { src: file.imageSignedUrl }
  if (uploadScope.value) uploadScope.value.files.push(tempFile)
}
const initData = () => {
  if (props.modelValue === values.value) return
  if (!props.modelValue) {
    values.value = ''
    if (uploadScope.value) uploadScope.value.reset()
    return
  }
  innerLoading.value = true
  const params = { pageSize: '-1', imageSignedUrlFlag: true, idList: (props.modelValue || '').split(',') }
  postAPI('/oss/api/ossUploadRecord/list', params, data => {
    values.value = props.modelValue
    for (const file of data.rows) formatFile(file)
    innerLoading.value = false
  }, () => {
    innerLoading.value = false
  })
}
watch(() => props.modelValue, () => initData(), { immediate: true })
const factoryFn = () => {
  return {
    url: url.value, method: 'POST', fieldName: 'files',
    formFields: [{
      name: 'serviceName', value: props.sName || 'factory'
    }, {
      name: 'bussinessModule', value: props.bName || 'factory'
    }, {
      name: 'publicRead', value: props.publicRead || false
    }]
  }
}

const getFiles = scope => {
  uploadScope.value = scope
}
const startUpload = () => innerLoading.value = true
const finishUpload = () => innerLoading.value = false
const added = files => {
  const { formatAdded } = props
  if (formatAdded && typeof formatAdded === 'function') formatAdded(files)
}
const failed = info => {
  if (info.xhr.response) {
    const error = JSON.parse(info.xhr.response)
    responseHandle(error.status, error)
  }
}
const formatValues = () => {
  const list = uploadScope.value.files
  values.value = list.map(el => el.id).join(',')
  const valueList = list.map(el => {
    if (el.xhr && el.xhr.response) return JSON.parse(el.xhr.response)
    return { data: [el] }
  }).map(el => el.data).flat()
  if (props.change) props.change(values.value, valueList)
  else emits('change', values.value, valueList)
  emits('update:modelValue', values.value || '')
}
const uploaded = info => {
  if (info.xhr.response) {
    const res = JSON.parse(info.xhr.response)
    if (res.code !== '0') {
      for (const el of info.files) {
        uploadScope.value.updateFileStatus(el, 'failed', el.size)
        uploadScope.value.removeFile(el)
      }
      responseHandle(res.code, res)
      return
    }
    const NUM = info.files.length
    for (let i = 0; i < NUM; i++) {
      info.files[i].id = res.data[i].id
    }
    formatValues()
  }
}
const viewPicture = (file, i) => {
  if (!(i > -1)) return
  if (file.__status === 'uploading') return
  pictureIndex.value = i
  dialogVisible.value = true
}
const downLoadFile = file => {
  innerLoading.value = true
  exportedStreamFile({
    dicUrl: '/oss/api/ossUploadRecord/download', dicQuery: { path: file.resultPath }, fileName: file.name,
    dicMethod: 'get', done: () => innerLoading.value = false
  })
}
const deleteFile = (file, index) => {
  createDialog({ msg: `${i18n.t('DelTips')}` }, () => {
    uploadScope.value.files.splice(index, 1)
    uploadScope.value.removeFile(file)
    formatValues()
  }, () => {})
}
</script>